﻿module Common
open System.Collections.Generic
open Microsoft.Office.Interop.Excel


//*** Temp value for a single cell (used from tree.eval)
let mutable currentCell:string = null
let currentSheet() = currentCell.Split('!').[0]

let mutable currentDip:string list = List.empty
let variables = new List<string>()

let resetInfo() =
    currentDip <- List.empty
    variables.Clear()


//*** GENERAL INFORMATIONS

let mutable wbk:Workbook = null
let mutable myStubs:Set<string> = Set.empty

let myInputs = new Dictionary<string,string>()

let getNewInputName =
    let count = ref 0 
    fun () -> count:=!count+1; "_inp"+(!count).ToString()

type InfoGen = { Dip: string[]; RelativeWinSz: int[] }
let myGeneralized = new Dictionary<string, InfoGen>()



/// Making a "range reduction" i.e. transform a range from complex form
/// (ex: "sheet1!A1:A3:C5 A3:A4:B3") to simple form ("sheet1!A3:B4")
let rangeSimplifier (rng:string) =
    let r0 = rng.Split('!')
    let sheet,rngOriginal = if r0.Length = 1 then (currentSheet(),r0.[0]) else r0.[0],r0.[1]
    let rang = 
        if rngOriginal.Split(':').Length>2 || rngOriginal.Contains(" ") then
            (wbk.Worksheets.[1]:?>Worksheet).Range(rngOriginal).Address(false,false,XlReferenceStyle.xlA1,true).Split('!').[1]
        else rngOriginal
    sheet + "!" + rang


let rng2var (r:string) =
    r.Replace('!','_').Replace(':','_')


/// Return the code corresponding to the coloumn number x, specified as parameter
/// Examples: col 1 ="A"; col 29 ="AC"; col 16384 = "XFD" 
let col x =
    if x>16384 || x<1 then invalidArg (sprintf "x (%d)"x) "Expected value between 1 and 2^14"
    let mutable str = ""
    let mutable numb = x
        
    while numb > 0 do
        let m = (numb-1)%26
        str <- (char (65+m)).ToString() + str
        numb <- (numb-m)/26
    str
   
 
let A1toPair (str:string) =
    let mutable i = str.Length-1
    while str.[i]<='9' do i<-i-1
    (str.[..i],int str.[i+1..]) 
   
/// Transform a A1-style range (without sheet-name) to (#Coloumn,#Row) numbers
let A1toCR (str:string) =
    let values = A1toPair str
    let col = "@@" + (fst values).ToUpper()
    let c = (int col.[col.Length-1]-64) + 26*(int col.[col.Length-2]-64) + 26*26*(int col.[col.Length-3]-64)
    (c, snd values)
 

 (*
let cellsFromRng (rng:string) =
    seq{
        let el = rng.Split('!')
        let ends = el.[1].Split(':')
        if ends.Length=1 then yield el.[0]+"!"+ends.[0]
        else
            let (col1,row1) = A1toCR ends.[0]
            let (col2,row2) = A1toCR ends.[1]
            for i in col1..col2 do
                for j in row1..row2 do
                    yield (el.[0]+"!"+(col i)+j.ToString())
        }
*)

let cellsFromColumn (column:string) =
    seq{
        let el = column.Split('!')
        let ends = el.[1].Split(':')
        if ends.Length=1 then yield el.[0]+"!"+ends.[0]
        else
            let (c,row1) = A1toPair ends.[0]
            let (_,row2) = A1toPair ends.[1]
            for j in row1..row2 do
                yield (el.[0] + "!" + c + j.ToString())
        }

/// Returns each column in the range
let columnsFromRng (rng:string) =
    seq{
        let el = rng.Split('!')
        let ends = el.[1].Split(':')
        if ends.Length=1 then yield el.[0]+"!"+ends.[0]
        else
            let (col1,row1) = A1toCR ends.[0]
            let (col2,row2) = A1toCR ends.[1]
            for i in col1..col2 do
                let column = col i
                yield (el.[0]+"!"+column+row1.ToString()+":"+column+row2.ToString())
    }

/// Returns in which element of 'ranges' there is 'cell', and it's distance
/// (assuming ranges.[i] in one column)
let cellWithinRanges (cell:string) (ranges:string[]) =
    let element = cell.Split('!')
    let (c0,r0) = A1toPair element.[1]
    let mutable i = 0
    let mutable ris = -1
    let mutable height = -1
    while ris<0 && i<ranges.Length do
        let group = ranges.[i].Split('!')
        if element.[0].Equals(group.[0]) then
            let celles = group.[1].Split(':')
            let c1,r1 = A1toPair (celles.[0])
            if celles.Length=1 then
                if c0.Equals(c1) && r0=r1 then ris<-i; height<-0
            else
                let c2,r2 = A1toPair (celles.[1])
                if c0.Equals(c1) && r0>=r1 && r0<=r2 then
                    ris<-i; height<-r0-r1
        i<-i+1
    (ris,height)
    


    //  OLD METHODS
  (*
/// From single range to Set<cell>
/// Example: singleRange("a!A1:B2:C3") = set ["a_A1","a_A2","a_B1","a_B2","a_C3"]
let private singleRange (rng:string) =
    let mutable cells = Set.empty<string>
    let r0 = rng.Split([|'!'|])
    let sheet,range = if r0.Length = 1 then (currentSheet(), r0.[0]) else (r0.[0]+"_", r0.[1])
    let limit = range.Split([|':'|])
    let mutable x = 0
    while x < limit.Length-1 do
        let (col1,row1) = A1toCR limit.[x]
        let (col2,row2) = A1toCR limit.[x+1]
        for i = col2 downto col1 do
            for j = row2 downto row1 do
                cells <- cells.Add(sheet+(col i)+j.ToString())
        x <- x+2 
    if limit.Length % 2 = 1 then 
        cells <- cells.Add(sheet+limit.[limit.Length-1])   
    cells


/// From a Range to a string that rapresent a seq of cells
/// Range Format supported:
///    r = (sheetName)? range (' '(sheetName)? range)* 
///                  with range = cellUpSx(':'cellDownDx)*
let cellsFromRange(r:string) =
    let rngs = r.Split([|' '|])
    let mutable cells = singleRange rngs.[0]
    for i in 1..rngs.Length-1 do
        cells <- Set.intersect (cells) (singleRange rngs.[i])
    cells
*)

